package org.word.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.word.exception.NotPostJsonReqException;
import org.word.model.*;
import org.word.model.definition.SwaggerModelDefinition;
import org.word.model.definition.SwaggerModelDefinitionRef;
import org.word.model.definition.SwaggerModelProperty;
import org.word.model.definition.SwaggerOverAllModel;
import org.word.model.path.*;
import org.word.service.*;
import org.word.utils.FileUtils;
import org.word.utils.JsonUtils;

import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author XiuYin.Cui
 * @Date 2018/1/12
 **/
@SuppressWarnings({"unchecked", "rawtypes"})
@Slf4j
@Service
public class CustomWordServiceImpl implements CustomWordService {

    @Autowired
    private RequestParamService requestParamService;

    @Autowired
    private ResponseParamService responseParamService;

    @Autowired
    private ModelDefinitionService modelDefinitionService;

    /**
     * {@link ModelDefinitionService#initDefinitionMap(java.util.LinkedHashMap)}
     * 是线程不安全的，所以干脆直接加锁了
     *
     * @param jsonFile
     * @return
     */
    @Override
    public synchronized Map<String, Object> tableList(String jsonStr) {


        Map<String, Object> resultMap = new HashMap<>();

        SwaggerOverAllModel swaggerOverAllModel = null;
        try {
            swaggerOverAllModel = JsonUtils.readValue(jsonStr, SwaggerOverAllModel.class);
        } catch (IOException e1) {
            log.error("e:{}", e1);
            throw new RuntimeException();
        }

        modelDefinitionService.initDefinitionMap(swaggerOverAllModel.getDefinitions());

        LinkedHashMap<String, PathVO> pathMap = swaggerOverAllModel.getPaths();
        processPaths(pathMap);

        List<CustomTable> tables = new ArrayList<>();

        //获取全部的请求
        List<PathVOForPost> pathVOForPostList = pathMap.values().stream().map(PathVO::getPost).collect(Collectors.toList());

        /**
         * 遍历每个请求,把每个请求，变成word中的一个table
         * 由于原开源项目中的Table的很多属性，我们用不到，所以直接重新定义一个简单的table，只包含必要的属性；
         * 方便后期维护
         *
         * 注意的是，我们这里没有去处理
         */
        for (PathVOForPost pathVOForPost : pathVOForPostList) {
            if (pathVOForPost == null) {
                continue;
            }
            log.info("start to process {}", pathVOForPost);
            /**
             * 获取请求参数集合
             * 不处理非post/get+json的请求，那种上传下载请求之类的，我们没处理，直接跳过了
             */
            List<Request> list;
            try {
                list = requestParamService.getRequestsParameterList(pathVOForPost);
            } catch (NotPostJsonReqException e) {
                log.error("非post+json请求，暂不处理:{}",pathVOForPost);
                continue;
            }

            /**
             * 获取响应参数集合
             */
            List<Response> responseList = responseParamService.getResponseParameterList(pathVOForPost);
            if (CollectionUtils.isEmpty(responseList)) {
                continue;
            }

            /**
             * 构造table的其他属性，比如接口路径、描述等
             */
            CustomTable table = constructTable(pathVOForPost, list, responseList);
            tables.add(table);
        }


        Map<String, List<CustomTable>> tableMap = tables.stream().parallel().collect(Collectors.groupingBy(CustomTable::getTag));
        resultMap.put("tableMap", new TreeMap<>(tableMap));
        resultMap.put("info", swaggerOverAllModel.getInfo());

        return resultMap;
    }


    /**
     * 字符串是否包含中文
     *
     * @param str 待校验字符串
     * @return true 包含中文字符 false 不包含中文字符
     */
    static Pattern p = Pattern.compile("[\u4E00-\u9FA5|\\！|\\，|\\。|\\（|\\）|\\《|\\》|\\“|\\”|\\？|\\：|\\；|\\【|\\】]");

    public static boolean isContainChinese(String str) {
        if (StringUtils.isEmpty(str)) {
            return false;
        }
        Matcher m = p.matcher(str);
        if (m.find()) {
            return true;
        }
        return false;
    }

    private CustomTable constructTable(PathVOForPost pathVOForPost, List<Request> requestParameterList, List<Response> responseParameterList) {
        //封装Table
        CustomTable table = new CustomTable();

        /**
         * 用tag将这个tag下的接口，聚在一起
         */
        table.setTag(pathVOForPost.getTags().get(0));
        /**
         * 接口描述,存在于summary或者description中
         */
        StringBuilder builder = new StringBuilder();
        String summary = pathVOForPost.getSummary();
        if (isContainChinese(summary)) {
            builder.append(summary);
        }

        if (isContainChinese(pathVOForPost.getDescription())) {
            String s = builder.toString();
            if (    StringUtils.isBlank(s)
                    || s.endsWith(",") ||
                    s.endsWith(".") ||
                    s.endsWith("。") ||
                    s.endsWith("，")) {
                builder.append(pathVOForPost.getDescription());
            } else {
                builder.append(",").append(pathVOForPost.getDescription());
            }
        }

        table.setApiDescription(builder.toString());


        /**
         * 接口url
         */
        table.setUrl(pathVOForPost.getPathUrl());

        /**
         * 设置请求参数
         */
        table.setRequestList(requestParameterList);

        table.setResponseList(responseParameterList);

        return table;
    }


    private void processPaths(LinkedHashMap<String, PathVO> pathVOLinkedHashMap) {
        for (Entry<String, PathVO> entry : pathVOLinkedHashMap.entrySet()) {
            PathVO vo = entry.getValue();
            PathVOForPost post = vo.getPost();
            if (post == null) {
                PathVOForPost get = vo.getGet();
                if (get == null) {
                    throw new RuntimeException();

                }
                post = get;

            }
            post.setPathUrl(entry.getKey());

            List<ParameterItem> parameters = post.getParameters();
            if (CollectionUtils.isEmpty(parameters)) {
                continue;
            }
            parameters.removeIf(new Predicate<ParameterItem>() {
                @Override
                public boolean test(ParameterItem parameterItem) {

                    if (Objects.equals(parameterItem.getIn(), "header")) {
                        return true;
                    }
                    return false;
                }
            });
            System.out.println(parameters);
        }
    }


    /**
     * 处理请求参数列表
     *
     * @param parameters
     * @return
     */
    private List<Request> processRequestList(List<LinkedHashMap> parameters) {
        List<Request> requestList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(parameters)) {
            for (Map<String, Object> param : parameters) {
                Object in = param.get("in");
                Request request = new Request();
                request.setName(String.valueOf(param.get("name")));
                request.setType(param.get("type") == null ? "object" : param.get("type").toString());
                request.setParamType(String.valueOf(in));
                // 考虑对象参数类型
                if (in != null && "body".equals(in)) {
                    Map<String, Object> schema = (Map) param.get("schema");
                    Object ref = schema.get("$ref");
                    // 数组情况另外处理
                    if (schema.get("type") != null && "array".equals(schema.get("type"))) {
                        ref = ((Map) schema.get("items")).get("$ref");
                    }
                    request.setParamType(ref == null ? "{}" : ref.toString());
                }
                // 是否必填
                request.setRequire(false);
                if (param.get("required") != null) {
                    request.setRequire((Boolean) param.get("required"));
                }
                // 参数说明
                request.setRemark(String.valueOf(param.get("description")));
                request.setParamType(request.getParamType().replaceAll("#/definitions/", ""));
                requestList.add(request);
            }
        }
        return requestList;
    }


    /**
     * 处理返回码列表
     *
     * @param responses 全部状态码返回对象
     * @return
     */
    private List<Response> processResponseCodeList(Map<String, Object> responses) {
        List<Response> responseList = new ArrayList<>();
        Iterator<Entry<String, Object>> resIt = responses.entrySet().iterator();
        while (resIt.hasNext()) {
            Entry<String, Object> entry = resIt.next();
            Response response = new Response();
            // 状态码 200 201 401 403 404 这样
            response.setName(entry.getKey());
            LinkedHashMap<String, Object> statusCodeInfo = (LinkedHashMap) entry.getValue();
            response.setDescription(String.valueOf(statusCodeInfo.get("description")));
            Object schema = statusCodeInfo.get("schema");
            if (schema != null) {
                Object originalRef = ((LinkedHashMap) schema).get("originalRef");
                response.setRemark(originalRef == null ? "" : originalRef.toString());
            }
            responseList.add(response);
        }
        return responseList;
    }

    /**
     * 处理返回属性列表
     *
     * @param responseObj
     * @param definitinMap
     * @return
     */
    private ModelAttr processResponseModelAttrs(Map<String, Object> responseObj, Map<String, ModelAttr> definitinMap) {
        Map<String, Object> schema = (Map<String, Object>) responseObj.get("schema");
        String type = (String) schema.get("type");
        String ref = null;
        //数组
        if ("array".equals(type)) {
            Map<String, Object> items = (Map<String, Object>) schema.get("items");
            if (items != null && items.get("$ref") != null) {
                ref = (String) items.get("$ref");
            }
        }
        //对象
        if (schema.get("$ref") != null) {
            ref = (String) schema.get("$ref");
        }

        //其他类型
        ModelAttr modelAttr = new ModelAttr();
        modelAttr.setType(StringUtils.defaultIfBlank(type, StringUtils.EMPTY));

        if (StringUtils.isNotBlank(ref) && definitinMap.get(ref) != null) {
            modelAttr = definitinMap.get(ref);
        }
        return modelAttr;
    }

    /**
     * 解析Definition
     *
     * @param map
     * @return
     */
    private Map<String, ModelAttr> parseDefinitions(Map<String, Object> map) {
        Map<String, Map<String, Object>> definitions = (Map<String, Map<String, Object>>) map.get("definitions");
        Map<String, ModelAttr> definitinMap = new HashMap<>(256);
        if (definitions != null) {
            Iterator<String> modelNameIt = definitions.keySet().iterator();
            while (modelNameIt.hasNext()) {
                String modeName = modelNameIt.next();
                Map<String, Object> modeProperties = (Map<String, Object>) definitions.get(modeName).get("properties");
                if (modeProperties == null) {
                    continue;
                }
                Iterator<Entry<String, Object>> mIt = modeProperties.entrySet().iterator();

                List<ModelAttr> attrList = new ArrayList<>();

                //解析属性
                while (mIt.hasNext()) {
                    Entry<String, Object> mEntry = mIt.next();
                    Map<String, Object> attrInfoMap = (Map<String, Object>) mEntry.getValue();
                    ModelAttr modeAttr = new ModelAttr();
                    modeAttr.setName(mEntry.getKey());
                    modeAttr.setType((String) attrInfoMap.get("type"));
                    if (attrInfoMap.get("format") != null) {
                        modeAttr.setType(modeAttr.getType() + "(" + attrInfoMap.get("format") + ")");
                    }
                    modeAttr.setType(StringUtils.defaultIfBlank(modeAttr.getType(), "object"));
                    modeAttr.setDescription((String) attrInfoMap.get("description"));
                    attrList.add(modeAttr);
                }

                ModelAttr modeAttr = new ModelAttr();
                Object title = definitions.get(modeName).get("title");
                Object description = definitions.get(modeName).get("description");
                modeAttr.setClassName(title == null ? "" : title.toString());
                modeAttr.setDescription(description == null ? "" : description.toString());
                modeAttr.setProperties(attrList);
                definitinMap.put("#/definitions/" + modeName, modeAttr);
            }
        }
        return definitinMap;
    }

    /**
     * 处理返回值
     *
     * @param responseObj
     * @return
     */
    private String processResponseParam(Map<String, Object> responseObj, Map<String, ModelAttr> definitinMap) throws JsonProcessingException {
        if (responseObj != null && responseObj.get("schema") != null) {
            Map<String, Object> schema = (Map<String, Object>) responseObj.get("schema");
            String type = (String) schema.get("type");
            String ref = null;
            // 数组
            if ("array".equals(type)) {
                Map<String, Object> items = (Map<String, Object>) schema.get("items");
                if (items != null && items.get("$ref") != null) {
                    ref = (String) items.get("$ref");
                }
            }
            // 对象
            if (schema.get("$ref") != null) {
                ref = (String) schema.get("$ref");
            }
            if (StringUtils.isNotEmpty(ref)) {
                ModelAttr modelAttr = definitinMap.get(ref);
                if (modelAttr != null && !CollectionUtils.isEmpty(modelAttr.getProperties())) {
                    Map<String, Object> responseMap = new HashMap<>(8);
                    for (ModelAttr subModelAttr : modelAttr.getProperties()) {
                        responseMap.put(subModelAttr.getName(), subModelAttr.getType());
                    }
                    return JsonUtils.writeJsonStr(responseMap);
                }
            }
        }
        return StringUtils.EMPTY;
    }

    /**
     * 封装请求体
     *
     * @param list
     * @param definitinMap
     * @return
     */
    private String processRequestParam(List<Request> list, Map<String, ModelAttr> definitinMap) throws IOException {
        Map<String, Object> paramMap = new HashMap<>(8);
        if (list != null && list.size() > 0) {
            for (Request request : list) {
                String name = request.getName();
                String type = request.getType();
                switch (type) {
                    case "string":
                        paramMap.put(name, "string");
                        break;
                    case "integer":
                        paramMap.put(name, 0);
                        break;
                    case "number":
                        paramMap.put(name, 0.0);
                        break;
                    case "boolean":
                        paramMap.put(name, true);
                        break;
                    case "body":
                    case "object":
                        ModelAttr modelAttr = definitinMap.get("#/definitions/" + request.getParamType());
                        if (modelAttr != null && !CollectionUtils.isEmpty(modelAttr.getProperties())) {
                            for (ModelAttr subModelAttr : modelAttr.getProperties()) {
                                paramMap.put(subModelAttr.getName(), subModelAttr.getType());
                            }
                            break;
                        }
                    default:
                        paramMap.put(name, null);
                        break;
                }
            }
        }
        return JsonUtils.writeJsonStr(paramMap);
    }
}
